﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using ThreeByte.ActiveDeck.AddIn.Content.Controls;
using System.Xml.Linq;
using ThreeByte.ActiveDeck.Config;
using System.ComponentModel;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using log4net;
using ThreeByte.Media;
using ThreeByte.ActiveDeck.Content;

namespace ThreeByte.ActiveDeck.AddIn.Config
{
    public class LayerConfig : INotifyPropertyChanged
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(LayerConfig));

        private static readonly XNamespace ns = Service.ServiceConfiguration.NAMESPACE;
        public static XName XML_NAME = ns + "Layer";



        //private static Dictionary<PowerPoint.Presentation, int> scopeInstance = new Dictionary<PowerPoint.Presentation, int>();

        //private static int ScopedInstance {
        //    get {
        //        PowerPoint.Presentation currentPresentation = Globals.ThisAddIn.Application.ActivePresentation;
        //        if(!(scopeInstance.ContainsKey(currentPresentation))) {
        //            scopeInstance[currentPresentation] = 0;
        //        }
        //        return scopeInstance[currentPresentation];
        //    }
        //    set {
        //        PowerPoint.Presentation currentPresentation = Globals.ThisAddIn.Application.ActivePresentation;
        //        if(!(scopeInstance.ContainsKey(currentPresentation))) {
        //            scopeInstance[currentPresentation] = 0;
        //        }
        //        scopeInstance[currentPresentation] = value;
        //    }
        //}

        public List<DesignerControl> Elements { get; private set; }

        private string _name;
        public string Name {
            get {
                return _name;
            }
            set {
                _name = value;
                NotifyPropertyChanged("Name");
            }
        }


        //private int _id;
        //public int ID {
        //    get {
        //        return _id;
        //    }
        //    private set {
        //        _id = value;
        //        NotifyPropertyChanged("ID");
        //    }
        //}

        private int _referenceSlideId;
        public int ReferenceSlideID {
            get {
                return _referenceSlideId;
            }
            private set {
                _referenceSlideId = value;
                NotifyPropertyChanged("ReferenceSlideID");
            }
        }

        private readonly PowerPoint.Slide ReferenceSlide;
        
        private bool _show;
        public bool Show {
            get {
                return _show;
            }
            set {
                _show = value;
                NotifyPropertyChanged("Show");
            }
        }

        private bool _pinned;
        public bool Pinned {
            get {
                return _pinned;
            }
            set {
                _pinned = value;
                NotifyPropertyChanged("Pinned");
            }
        }

        private string _condition;
        public string Condition {
            get {
                return _condition;
            }
            set {
                _condition = value;
                NotifyPropertyChanged("Condition");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName) {
            if(PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        private LayerConfig() {
            Elements = new List<DesignerControl>();
            Show = true; //By default
        }


        public LayerConfig(PowerPoint.Slide slide)
            : this() {
                ReferenceSlide = slide;
            ReferenceSlideID = slide.SlideID;
            Name = string.Format("Slide {0}", ReferenceSlide.SlideNumber);
        }

        public void Refresh(bool force = true) {

            //TODO: Is this too clever?
            //Short-circuit if it is not required and there are already some elements here
            if(!force && Elements.Count > 0) {
                return;
            }

            //Resync from the specified slide
            log.DebugFormat("Refreshing Layer: {0}", ReferenceSlide.SlideNumber);
            Name = string.Format("Slide {0}", ReferenceSlide.SlideNumber);
            Elements.Clear();

            Orientation layoutOrientation = SlideUtil.GetOrientation((int)Math.Round(ReferenceSlide.Master.Width), (int)Math.Round(ReferenceSlide.Master.Height));

            List<DesignerControl> newElements = SlideUtil.GetLayerElements(ReferenceSlide, layoutOrientation);
            foreach(DesignerControl c in newElements) {
                Elements.Add(c);
            }
        }

        public XElement ToXml() {
            XElement node = new XElement(XML_NAME);

            node.Add(new XAttribute("ReferenceSlideID", ReferenceSlideID));
            node.Add(new XAttribute("Name", Name));
            node.Add(new XAttribute("Show", Show));
            node.Add(new XAttribute("Pinned", Pinned));
            if(Condition != null) {
                node.Add(new XAttribute("Condition", Condition));
            }

            //foreach(DesignerControl c in Elements) {
            //    node.Add(c.ToXml());
            //}

            return node;
        }


        private static Dictionary<PowerPoint.Presentation, Dictionary<int, LayerConfig>> cachedLayers = new Dictionary<PowerPoint.Presentation, Dictionary<int, LayerConfig>>();
        private static Dictionary<int, LayerConfig> ScopedLayerSet {
            get {
                PowerPoint.Presentation currentPresentation = Globals.ThisAddIn.Application.ActivePresentation;
                if(!(cachedLayers.ContainsKey(currentPresentation))) {
                    cachedLayers[currentPresentation] = new Dictionary<int, LayerConfig>();
                }
                return cachedLayers[currentPresentation];
            }
            //set {
            //    PowerPoint.Presentation currentPresentation = Globals.ThisAddIn.Application.ActivePresentation;
            //    if(!(scopeInstance.ContainsKey(currentPresentation))) {
            //        scopeInstance[currentPresentation] = 0;
            //    }
            //    scopeInstance[currentPresentation] = value;
            //}
        }

        public static void ClearLayerCache(PowerPoint.Presentation pres) {
            if(cachedLayers.ContainsKey(pres)) {
                cachedLayers.Remove(pres);
            }
        }

        public static LayerConfig FromXml(XElement node, Dictionary<Guid, GraphicResource> resources) {

            int slideId = int.Parse(node.Attribute("ReferenceSlideID").Value);

            if(ScopedLayerSet.ContainsKey(slideId)){
                //log.DebugFormat("Returning Cached Layer: {0}", slideId);
                return ScopedLayerSet[slideId];
            }

            log.DebugFormat("Constructing new slide from reference ID: {0}", slideId);
            LayerConfig newLayer = new LayerConfig(Globals.ThisAddIn.Application.ActivePresentation.Slides.FindBySlideID(slideId));
            //newLayer.ID = int.Parse(node.Attribute("ID").Value);
            //ScopedInstance = Math.Max(ScopedInstance, newLayer.ID);

            if(node.Attribute("Show") != null) {
                newLayer.Show = bool.Parse(node.Attribute("Show").Value);
            }
            if(node.Attribute("Pinned") != null) {
                newLayer.Pinned = bool.Parse(node.Attribute("Pinned").Value);
            }
            if(node.Attribute("Condition") != null) {
                newLayer.Condition = node.Attribute("Condition").Value;
            }

            //newLayer.Refresh();
            ScopedLayerSet[slideId] = newLayer;
            //foreach (XElement n in node.Elements(ns + "DesignerControl")) {
            //    try {
            //        DesignerControl newControl = DesignerControl.FromXml(n, resources);
            //        newLayer.Elements.Add(newControl);
            //    } catch (Exception ex) {
            //        log.Error("Could not create a designer control in LayerConfig...continuing", ex);
            //    }
            //}
            return newLayer;
        }

        public PixelSpace GetBoundingBox(Orientation layoutOrientation) {
            if(Pinned) {
                PixelSpace boundingBox = null;
                foreach(DesignerControl c in Elements) {
                    if(boundingBox == null){
                        boundingBox = c.CurrentPixelSpace;
                    } else {
                        boundingBox = PixelSpace.GetContainingSpace(boundingBox, c.CurrentPixelSpace);
                    }
                }
                return (boundingBox ?? new PixelSpace());  //Don't return null
            } else {
                //Return the whole screen
                //(AutoHide will be enabled)
                PixelSpace ps = new PixelSpace() { X = 0, Y = 0, H = 1024, W = 768 };
                if(layoutOrientation == Orientation.LandscapeLeft || layoutOrientation == Orientation.LandscapeRight) {
                    ps = new PixelSpace() { X = 0, Y = 0, H = 768, W = 1024 };
                }
                return ps;
            }
        }
    }
}
